import CryptoJS from 'crypto-js';
import pako from 'pako';
import type { BaseModel } from '../../model/base';

export interface BaseSnapshot {
  version: number;
}

export class SaveSystem {
  private models: BaseModel[];
  /** 注意：不能更改，改了加解密存档会报错 */
  private key = 'jianlouxiuxian';

  constructor(models: BaseModel[]) {
    this.models = models;
  }

  private encryptAndZip(text: string) {
    const compressed = pako.deflate(text);
    const compressedBase64 = btoa(
      String.fromCharCode(...new Uint8Array(compressed))
    );
    const encrypted = CryptoJS.AES.encrypt(
      compressedBase64,
      this.key
    ).toString();
    return encrypted;
  }

  private unzipAndDecrypt(b64: string) {
    const decrypted = CryptoJS.AES.decrypt(b64, this.key);
    const decryptedBase64 = decrypted.toString(CryptoJS.enc.Utf8);
    const binaryString = atob(decryptedBase64);
    const bytes = new Uint8Array(binaryString.length);
    for (let i = 0; i < binaryString.length; i++) {
      bytes[i] = binaryString.charCodeAt(i);
    }
    const decompressed = pako.inflate(bytes, { to: 'string' });
    return decompressed;
  }

  public unzipAndDecryptSave(data: string) {
    let jsonText: string;
    try {
      jsonText = this.unzipAndDecrypt(data);
    } catch {
      jsonText = data;
    }
    try {
      const data: Record<string, any> = JSON.parse(jsonText);
      return data;
    } catch (error) {
      throw new Error('存档解密失败');
    }
  }

  public encryptAndZipSave(data: any) {
    const text = JSON.stringify(data);
    return this.encryptAndZip(text);
  }

  public save() {
    const data: Record<string, any> = {};
    for (const m of this.models) {
      if (m.save) {
        data[m.name] = m.save();
      }
    }
    const text = JSON.stringify(data);
    return this.encryptAndZip(text);
  }

  public load(raw: string) {
    if (raw === '{}') {
      return;
    }

    let jsonText: string;
    try {
      jsonText = this.unzipAndDecrypt(raw);
    } catch {
      jsonText = raw;
    }
    try {
      const data: Record<string, any> = JSON.parse(jsonText);
      console.log(data);
      for (const m of this.models) {
        if (data[m.name] !== undefined) {
          m.loadRaw(data[m.name]);
        }
      }
    } catch (e) {
      console.error(e);
      throw new Error('加载存档失败');
    }
  }
}
